/*
 * Copyright (c) 2012, Marcin Czekaj
 * All rights reserved.
 *
 * This software is open-source under the BSD license; see LICENSE.TXT for a description.
 */
package pl.edu.uj.fais.binding;

import java.awt.Cursor;
import java.awt.event.MouseEvent;

import pl.edu.uj.fais.binding.plugin.ConstrainedEditingPlugin;
import pl.edu.uj.fais.controller.Controller;
import pl.edu.uj.fais.graph.GraphStyle;
import pl.edu.uj.fais.graph.elements.Link;
import pl.edu.uj.fais.graph.elements.Node;
import pl.edu.uj.fais.gui.GraphCanvas;
import pl.edu.uj.fais.mode.GraphMode;
import edu.uci.ics.jung.visualization.control.PickingGraphMousePlugin;
import edu.uci.ics.jung.visualization.control.PluggableGraphMouse;
import edu.uci.ics.jung.visualization.control.ScalingControl;
import edu.uci.ics.jung.visualization.control.ScalingGraphMousePlugin;
import edu.uci.ics.jung.visualization.control.TranslatingGraphMousePlugin;

public class MouseBinding {
    
    private static final Cursor CURSOR_ARROW = new Cursor(Cursor.DEFAULT_CURSOR);
    private static final Cursor CURSOR_MOVE = new Cursor(Cursor.MOVE_CURSOR);
    private static final Cursor CURSOR_HAND = new Cursor(Cursor.HAND_CURSOR);
    private static final Cursor CURSOR_CROSS = new Cursor(Cursor.CROSSHAIR_CURSOR);
    
    private static MouseBinding instance;
    
    private PluggableGraphMouse graphMouse;
    
    private TranslatingGraphMousePlugin translatePlugin;
    private ScalingGraphMousePlugin scalePlugin;
    private PickingGraphMousePlugin<Node, Link> pickPlugin;
    private ConstrainedEditingPlugin editPlugin;
    
    public static MouseBinding getInstance() {
        
        if (instance == null) {
            instance = new MouseBinding();
        }
        
        return instance;
    }
    
    private MouseBinding() {
        graphMouse = new PluggableGraphMouse();
        initPlugins();
    }
    
    public void bind(Controller controller, GraphCanvas canvas) {
        
        if (controller.getGraphMode() == GraphMode.HYPERGRAPH) {
            editPlugin.setConstrained(true);
        } else {
            editPlugin.setConstrained(false);
        }
        
        updateState(controller, canvas);
        
        canvas.setGraphMouse(graphMouse);
    }
    
    private void updateState(Controller controller, GraphCanvas canvas) {
        
        removeAllPlugins();
        
        switch (controller.getMouseMode()) {
            case TRANSLATE:
                
                // plug-ins
                graphMouse.add(scalePlugin);
                graphMouse.add(translatePlugin);
                
                // cursor
                canvas.setCursor(CURSOR_MOVE);
                
                // settings
                canvas.setEdgeSelection(false);
                
                break;
            case EDIT:
                
                // plug-ins
                graphMouse.add(pickPlugin);
                
                // cursor
                canvas.setCursor(CURSOR_HAND);
                
                // settings
                canvas.setEdgeSelection(false);
                
                break;
            case ADD:
                
                // plug-ins
                graphMouse.add(editPlugin);
                
                // cursor
                canvas.setCursor(CURSOR_CROSS);
                
                // settings
                controller.unselectAll();
                canvas.setEdgeSelection(false);
                
                break;
            case NONE:
                
                // cursor
                canvas.setCursor(CURSOR_ARROW);
                
                // settings
                canvas.setEdgeSelection(false);
                
                break;
            case LINKS:
                
                // plug-ins
                graphMouse.add(pickPlugin);
                
                // cursor
                canvas.setCursor(CURSOR_ARROW);
                
                // settings
                controller.unselectAll();
                canvas.setEdgeSelection(true);
                
                break;
            default:
                break;
        }
    }
    
    private void initPlugins() {
        initTranslatePlugin();
        initScalePlugin();
        initPickPlugin();
        initEditPlugin();
    }
    
    private void initTranslatePlugin() {
        // @formatter:off
        translatePlugin = new TranslatingGraphMousePlugin();
        translatePlugin.setModifiers(MouseEvent.BUTTON1_MASK |
                                     MouseEvent.SHIFT_MASK);
        // @formatter:on
    }
    
    private void initScalePlugin() {
        
        Controller controller = Controller.getInstance();
        ScalingControl scaler = controller.getScaler();
        
        float out = GraphStyle.ZOOM_OUT_FACTOR;
        float in = GraphStyle.ZOOM_IN_FACTOR;
        
        scalePlugin = new ScalingGraphMousePlugin(scaler, 0, out, in);
        scalePlugin.setModifiers(MouseEvent.SHIFT_MASK);
    }
    
    private void initPickPlugin() {
        pickPlugin = new PickingGraphMousePlugin<>();
        pickPlugin.setLensColor(GraphStyle.NODE_LABEL_SELECTED);
    }
    
    private void initEditPlugin() {
        editPlugin = new ConstrainedEditingPlugin();
        editPlugin.setModifiers(MouseEvent.CTRL_MASK);
    }
    
    private void removeAllPlugins() {
        graphMouse.remove(translatePlugin);
        graphMouse.remove(scalePlugin);
        graphMouse.remove(pickPlugin);
        graphMouse.remove(editPlugin);
    }
}
